package com.lagou.ipersistence.builder.xml;

import com.lagou.ipersistence.builder.BaseBuilder;
import com.lagou.ipersistence.builder.BuilderException;
import com.lagou.ipersistence.builder.MapperBuilderAssistant;
import com.lagou.ipersistence.io.Resources;
import com.lagou.ipersistence.mapping.MappedStatement;
import com.lagou.ipersistence.parsing.XNode;
import com.lagou.ipersistence.parsing.XPathParser;
import com.lagou.ipersistence.session.Configuration;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.InputStream;
import java.util.List;
import java.util.Map;

public class XMLMapperBuilder extends BaseBuilder {
    private final XPathParser parser;
    private final Map<String, XNode> sqlFragments;
    private final String resource;
    private final MapperBuilderAssistant builderAssistant;
    
    public XMLMapperBuilder(Configuration configuration, InputStream in, String resource, Map<String, XNode> sqlFragments) {
        super(configuration);
        parser = new XPathParser(in, false, new XMLMapperEntityResolver());
        this.resource = resource;
        this.sqlFragments = sqlFragments;
        builderAssistant = new MapperBuilderAssistant(configuration, resource);
    }
    
    public void parse() {
        configurationElement(parser.evalNode("/mapper"));
        bindMapperForNamespace();
    }
    
    private void bindMapperForNamespace() {
        String namespace = builderAssistant.getCurrentNamespace();
        if (namespace != null) {
            Class<?> boundType = null;
            try {
                boundType = Resources.classForName(namespace);
            } catch (ClassNotFoundException e) {
                // do nothing
            }
            if (boundType != null) {
                if (!configuration.hasMapper(boundType)) {
                    configuration.addLoadedResources("namespace:" + namespace);
                    configuration.addMapper(boundType);
                }
            }
        }
    }
    
    private void configurationElement(XNode context) {
        try {
            String namespace = context.getStringAttribute("namespace");
            if (namespace == null || namespace.isEmpty()) {
                throw new BuilderException("Mapper的命名空间不能为空!");
            }
            builderAssistant.setCurrentNamespace(namespace);
            // 构建MappedStatement
            buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
        } catch (Exception e) {
            throw new BuilderException("解析Mapper文件'" + resource + "'出错, 原因: " + e, e);
        }
    }
    
    private void buildStatementFromContext(List<XNode> list) {
        for (XNode context : list) {
            XMLStatementBuilder builder = new XMLStatementBuilder(configuration, builderAssistant, context);
            builder.parseStatementNode();
        }
    }
    
    private void cacheElement(XNode context) {
        if (context == null) {
            return;
        }
        String type = context.getStringAttribute("type", "PERPETUAL");
    }
    
    /**
     * 解析mapper.xml文件, 并将数据放置到Configuration配置容器中
     *
     * @param in 文件输入流
     */
    public void parse(InputStream in) throws DocumentException {
        Document doc = new SAXReader().read(in);
        Element rootEle = doc.getRootElement();
        // 获取到select标签的元素
        List<Element> list = rootEle.selectNodes("//select|delete|insert|update");
        // 解析当前文根元素的namespace
        String namespace = rootEle.attributeValue("namespace");
        for (Element ele : list) {
            String id = ele.attributeValue("id");
            String parameterType = ele.attributeValue("parameterType");
            String resultType = ele.attributeValue("resultType");
            MappedStatement mst = new MappedStatement();
            mst.setId(id);
            mst.setParameterType(parameterType);
            mst.setResultType(resultType);
            
            String key = namespace + "." + id;
            configuration.getMappedStatementMap().put(key, mst);
        }
    }
}
